home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 24 / CU Amiga Magazine's Super CD-ROM 24 (1998)(EMAP Images)(GB)(Track 1 of 2)[!][issue 1998-07].iso / CUCD / Utilities / vim-5.1 / src / charset.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-03-30  |  16.7 KB  |  824 lines

  1. /* vi:set ts=8 sts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved    by Bram Moolenaar
  4.  *
  5.  * Do ":help uganda"  in Vim to read copying and usage conditions.
  6.  * Do ":help credits" in Vim to see a list of people who contributed.
  7.  */
  8.  
  9. #include "vim.h"
  10.  
  11. /*
  12.  * chartab[] is used
  13.  * - to quickly recognize ID characters
  14.  * - to quickly recognize file name characters
  15.  * - to quickly recognize printable characters
  16.  * - to store the size of a character on the screen: Printable is 1 position,
  17.  *   2 otherwise
  18.  */
  19. static int    chartab_initialized = FALSE;
  20.  
  21. /*
  22.  * init_chartab(): Fill chartab[] with flags for ID and file name characters
  23.  * and the size of characters on the screen (1 or 2 positions).
  24.  * Also fills b_chartab[] with flags for keyword characters for current
  25.  * buffer.
  26.  *
  27.  * Return FAIL if 'iskeyword', 'isident', 'isfname' or 'isprint' option has an
  28.  * error, OK otherwise.
  29.  */
  30.     int
  31. init_chartab()
  32. {
  33.     int        c;
  34.     int        c2;
  35.     char_u  *p;
  36.     int        i;
  37.     int        tilde;
  38.     int        do_isalpha;
  39.  
  40.     /*
  41.      * Set the default size for printable characters:
  42.      * From <Space> to '~' is 1 (printable), others are 2 (not printable).
  43.      * This also inits all 'isident' and 'isfname' flags to FALSE.
  44.      */
  45.     c = 0;
  46.     while (c < ' ')
  47.     chartab[c++] = 2;
  48.     while (c <= '~')
  49.     chartab[c++] = 1 + CHAR_IP;
  50. #ifdef FKMAP
  51.     if (p_altkeymap)
  52.     {
  53.     while (c < YE)
  54.         chartab[c++] = 1 + CHAR_IP;
  55.     }
  56. #endif
  57.     while (c < 256)
  58.     chartab[c++] = 2;
  59.  
  60.     /*
  61.      * Init word char flags all to FALSE
  62.      */
  63.     if (curbuf != NULL)
  64.     for (c = 0; c < 256; ++c)
  65.         curbuf->b_chartab[c] = FALSE;
  66.  
  67. #ifdef LISPINDENT
  68.     /*
  69.      * In lisp mode the '-' character is included in keywords.
  70.      */
  71.     if (curbuf->b_p_lisp)
  72.     curbuf->b_chartab['-'] = TRUE;
  73. #endif
  74.  
  75.     /* Walk through the 'isident', 'iskeyword', 'isfname' and 'isprint'
  76.      * options Each option is a list of characters, character numbers or
  77.      * ranges, separated by commas, e.g.: "200-210,x,#-178,-"
  78.      */
  79.     for (i = 0; i < 4; ++i)
  80.     {
  81.     if (i == 0)
  82.         p = p_isi;            /* first round: 'isident' */
  83.     else if (i == 1)
  84.         p = p_isp;            /* second round: 'isprint' */
  85.     else if (i == 2)
  86.         p = p_isf;            /* third round: 'isfname' */
  87.     else    /* i == 3 */
  88.         p = curbuf->b_p_isk;    /* fourth round: 'iskeyword' */
  89.  
  90.     while (*p)
  91.     {
  92.         tilde = FALSE;
  93.         do_isalpha = FALSE;
  94.         if (*p == '^' && p[1] != NUL)
  95.         {
  96.         tilde = TRUE;
  97.         ++p;
  98.         }
  99.         if (isdigit(*p))
  100.         c = getdigits(&p);
  101.         else
  102.         c = *p++;
  103.         c2 = -1;
  104.         if (*p == '-' && p[1] != NUL)
  105.         {
  106.         ++p;
  107.         if (isdigit(*p))
  108.             c2 = getdigits(&p);
  109.         else
  110.             c2 = *p++;
  111.         }
  112.         if (c <= 0 || (c2 < c && c2 != -1) || c2 >= 256 ||
  113.                             !(*p == NUL || *p == ','))
  114.         return FAIL;
  115.  
  116.         if (c2 == -1)    /* not a range */
  117.         {
  118.         /*
  119.          * A single '@' (not "@-@"):
  120.          * Decide on letters being ID/printable/keyword chars with
  121.          * standard function isalpha(). This takes care of locale.
  122.          */
  123.         if (c == '@')
  124.         {
  125.             do_isalpha = TRUE;
  126.             c = 1;
  127.             c2 = 255;
  128.         }
  129.         else
  130.             c2 = c;
  131.         }
  132.         while (c <= c2)
  133.         {
  134.         if (!do_isalpha || isalpha(c)
  135. #ifdef FKMAP
  136.             || (p_altkeymap && (F_isalpha(c) || F_isdigit(c)))
  137. #endif
  138.                 )
  139.         {
  140.             if (i == 0)            /* (re)set ID flag */
  141.             {
  142.             if (tilde)
  143.                 chartab[c] &= ~CHAR_ID;
  144.             else
  145.                 chartab[c] |= CHAR_ID;
  146.             }
  147.             else if (i == 1)        /* set printable to 1 or 2 */
  148.             {
  149.             if (c < ' ' || c > '~'
  150. #ifdef FKMAP
  151.                 || (p_altkeymap
  152.                         && (F_isalpha(c) || F_isdigit(c)))
  153. #endif
  154.                 )
  155.             {
  156.                 if (tilde)
  157.                 {
  158.                 chartab[c] = (chartab[c] & ~CHAR_MASK) + 2;
  159.                 chartab[c] &= ~CHAR_IP;
  160.                 }
  161.                 else
  162.                 {
  163.                 chartab[c] = (chartab[c] & ~CHAR_MASK) + 1;
  164.                 chartab[c] |= CHAR_IP;
  165.                 }
  166.             }
  167.             }
  168.             else if (i == 2)        /* (re)set fname flag */
  169.             {
  170.             if (tilde)
  171.                 chartab[c] &= ~CHAR_IF;
  172.             else
  173.                 chartab[c] |= CHAR_IF;
  174.             }
  175.             else /* i == 3 */        /* (re)set keyword flag */
  176.             curbuf->b_chartab[c] = !tilde;
  177.         }
  178.         ++c;
  179.         }
  180.         p = skip_to_option_part(p);
  181.     }
  182.     }
  183.     chartab_initialized = TRUE;
  184.     return OK;
  185. }
  186.  
  187. /*
  188.  * Translate any special characters in buf[bufsize].
  189.  * If there is not enough room, not all characters will be translated.
  190.  */
  191.     void
  192. trans_characters(buf, bufsize)
  193.     char_u  *buf;
  194.     int        bufsize;
  195. {
  196.     int        len;        /* length of string needing translation */
  197.     int        room;        /* room in buffer after string */
  198.     char_u  *new;        /* translated character */
  199.     int        new_len;        /* length of new[] */
  200.  
  201.     len = STRLEN(buf);
  202.     room = bufsize - len;
  203.     while (*buf)
  204.     {
  205.     new = transchar(*buf);
  206.     new_len = STRLEN(new);
  207.     if (new_len > 1)
  208.     {
  209.         room -= new_len - 1;
  210.         if (room <= 0)
  211.         return;
  212.         vim_memmove(buf + new_len, buf + 1, (size_t)len);
  213.     }
  214.     vim_memmove(buf, new, (size_t)new_len);
  215.     buf += new_len;
  216.     --len;
  217.     }
  218. }
  219.  
  220. /*
  221.  * Catch 22: chartab[] can't be initialized before the options are
  222.  * initialized, and initializing options may cause transchar() to be called!
  223.  * When chartab_initialized == FALSE don't use chartab[].
  224.  */
  225.     char_u *
  226. transchar(c)
  227.     int     c;
  228. {
  229.     static char_u   buf[5];
  230.     int            i;
  231.  
  232.     i = 0;
  233.     if (IS_SPECIAL(c))        /* special key code, display as ~@ char */
  234.     {
  235.     buf[0] = '~';
  236.     buf[1] = '@';
  237.     i = 2;
  238.     c = K_SECOND(c);
  239.     }
  240.  
  241.     if ((!chartab_initialized && ((c >= ' ' && c <= '~')
  242. #ifdef FKMAP
  243.             || F_ischar(c)
  244. #endif
  245.         )) || (chartab[c] & CHAR_IP))        /* printable character */
  246.     {
  247.     buf[i] = c;
  248.     buf[i + 1] = NUL;
  249.     }
  250.     else
  251.     transchar_nonprint(buf + i, c);
  252.     return buf;
  253. }
  254.  
  255.     void
  256. transchar_nonprint(buf, c)
  257.     char_u    *buf;
  258.     int        c;
  259. {
  260.     if (c <= 0x7f)                    /* 0x00 - 0x1f and 0x7f */
  261.     {
  262.     if (c == NL)
  263.         c = NUL;            /* we use newline in place of a NUL */
  264.     buf[0] = '^';
  265.     buf[1] = c ^ 0x40;        /* DEL displayed as ^? */
  266.     buf[2] = NUL;
  267.     }
  268.     else if (c >= ' ' + 0x80 && c <= '~' + 0x80)    /* 0xa0 - 0xfe */
  269.     {
  270.     buf[0] = '|';
  271.     buf[1] = c - 0x80;
  272.     buf[2] = NUL;
  273.     }
  274.     else                        /* 0x80 - 0x9f and 0xff */
  275.     {
  276.     buf[0] = '~';
  277.     buf[1] = (c - 0x80) ^ 0x40;    /* 0xff displayed as ~? */
  278.     buf[2] = NUL;
  279.     }
  280. }
  281.  
  282. /*
  283.  * return the number of characters 'c' will take on the screen
  284.  * This is used very often, keep it fast!!!
  285.  */
  286.     int
  287. charsize(c)
  288.     int c;
  289. {
  290.     if (IS_SPECIAL(c))
  291.     return (chartab[K_SECOND(c)] & CHAR_MASK) + 2;
  292.     return (chartab[c] & CHAR_MASK);
  293. }
  294.  
  295. /*
  296.  * Return the number of characters string 's' will take on the screen,
  297.  * counting TABs as two characters: "^I".
  298.  */
  299.     int
  300. vim_strsize(s)
  301.     char_u *s;
  302. {
  303.     int        len = 0;
  304.  
  305.     while (*s)
  306.     len += charsize(*s++);
  307.     return len;
  308. }
  309.  
  310. /*
  311.  * Return the number of characters 'c' will take on the screen, taking
  312.  * into account the size of a tab.
  313.  * Use a define to make it fast, this is used very often!!!
  314.  * Also see getvcol() below.
  315.  */
  316.  
  317. #define RET_WIN_BUF_CHARTABSIZE(wp, buf, c, col) \
  318.     if ((c) == TAB && !(wp)->w_p_list) \
  319.     { \
  320.     int ts; \
  321.     ts = (buf)->b_p_ts; \
  322.     return (int)(ts - (col % ts)); \
  323.     } \
  324.     else \
  325.     return charsize(c);
  326.  
  327.     int
  328. chartabsize(c, col)
  329.     int        c;
  330.     colnr_t    col;
  331. {
  332.     RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, c, col)
  333. }
  334.  
  335.     int
  336. win_chartabsize(wp, c, col)
  337.     WIN        *wp;
  338.     int        c;
  339.     colnr_t    col;
  340. {
  341.     RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, c, col)
  342. }
  343.  
  344. /*
  345.  * return the number of characters the string 's' will take on the screen,
  346.  * taking into account the size of a tab
  347.  */
  348.     int
  349. linetabsize(s)
  350.     char_u    *s;
  351. {
  352.     colnr_t    col = 0;
  353.  
  354.     while (*s != NUL)
  355.     col += lbr_chartabsize(s++, col);
  356.     return (int)col;
  357. }
  358.  
  359. /*
  360.  * Like linetabsize(), but for a given window instead of the current one.
  361.  */
  362.     int
  363. win_linetabsize(wp, s)
  364.     WIN        *wp;
  365.     char_u    *s;
  366. {
  367.     colnr_t    col = 0;
  368.  
  369.     while (*s != NUL)
  370.     col += win_lbr_chartabsize(wp, s++, col, NULL);
  371.     return (int)col;
  372. }
  373.  
  374. /*
  375.  * return TRUE if 'c' is a normal identifier character
  376.  * letters and characters from 'isident' option.
  377.  */
  378.     int
  379. vim_isIDc(c)
  380.     int c;
  381. {
  382.     return (c < 0x100 && (chartab[c] & CHAR_ID));
  383. }
  384.  
  385. /*
  386.  * return TRUE if 'c' is a keyword character: Letters and characters from
  387.  * 'iskeyword' option for current buffer.
  388.  */
  389.     int
  390. vim_iswordc(c)
  391.     int c;
  392. {
  393.     return (c < 0x100 && curbuf->b_chartab[c]);
  394. }
  395.  
  396.     int
  397. vim_iswordc_buf(c, buf)
  398.     int c;
  399.     BUF    *buf;
  400. {
  401.     return (c < 0x100 && buf->b_chartab[c]);
  402. }
  403.  
  404. /*
  405.  * return TRUE if 'c' is a valid file-name character
  406.  */
  407.     int
  408. vim_isfilec(c)
  409.     int    c;
  410. {
  411.     return (c < 0x100 && (chartab[c] & CHAR_IF));
  412. }
  413.  
  414. /*
  415.  * return TRUE if 'c' is a printable character
  416.  */
  417.     int
  418. vim_isprintc(c)
  419.     int c;
  420. {
  421.     return (c < 0x100 && (chartab[c] & CHAR_IP));
  422. }
  423.  
  424. /*
  425.  * return TRUE if 'c' is a printable character
  426.  *
  427.  * No check for (c < 0x100) to make it a bit faster.
  428.  * This is the most often used function, keep it fast!
  429.  */
  430.     int
  431. safe_vim_isprintc(c)
  432.     int c;
  433. {
  434.     return (chartab[c] & CHAR_IP);
  435. }
  436.  
  437. /*
  438.  * like chartabsize(), but also check for line breaks on the screen
  439.  */
  440.     int
  441. lbr_chartabsize(s, col)
  442.     unsigned char   *s;
  443.     colnr_t        col;
  444. {
  445.     if (!curwin->w_p_lbr && *p_sbr == NUL)
  446.     {
  447.     RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, *s, col)
  448.     }
  449.  
  450.     return win_lbr_chartabsize(curwin, s, col, NULL);
  451. }
  452.  
  453. /*
  454.  * This function is used very often, keep it fast!!!!
  455.  * Warning: *head is only set if it's a non-zero value, init to 0 before
  456.  * calling.
  457.  */
  458.     int
  459. win_lbr_chartabsize(wp, s, col, head)
  460.     WIN            *wp;
  461.     unsigned char   *s;
  462.     colnr_t        col;
  463.     int            *head;
  464. {
  465.     int        c = *s;
  466.     int        size;
  467.     colnr_t col2;
  468.     colnr_t colmax;
  469.     int        added;
  470.     int     numberextra;
  471.  
  472. /*
  473.  * No 'linebreak' and 'showbreak': return quickly.
  474.  */
  475.     if (!wp->w_p_lbr && *p_sbr == NUL)
  476.     {
  477.     RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, c, col)
  478.     }
  479.  
  480. /*
  481.  * First get normal size, without 'linebreak'
  482.  */
  483.     size = win_chartabsize(wp, c, col);
  484. /*
  485.  * If 'linebreak' set check at a blank before a non-blank if the line needs a
  486.  * break here
  487.  */
  488.     if (wp->w_p_lbr && vim_isbreak(c) && !vim_isbreak(s[1]) &&
  489.                            !wp->w_p_list && wp->w_p_wrap)
  490.     {
  491.     numberextra = wp->w_p_nu? 8: 0;
  492.     /* count all characters from first non-blank after a blank up to next
  493.      * non-blank after a blank */
  494.     col2 = col;
  495.     colmax = (((col + numberextra) / Columns) + 1) * Columns;
  496.     while ((c = *++s) != NUL && (vim_isbreak(c)
  497.         || (!vim_isbreak(c) && (col2 == col || !vim_isbreak(s[-1])))))
  498.     {
  499.         col2 += win_chartabsize(wp, c, col2);
  500.         if (col2 + numberextra >= colmax)        /* doesn't fit */
  501.         {
  502.         size = Columns - ((col + numberextra) % Columns);
  503.         break;
  504.         }
  505.     }
  506.     }
  507.  
  508. /*
  509.  * May have to add something for 'showbreak' string at start of line
  510.  * Set *head to the size of what we add.
  511.  */
  512.     added = 0;
  513.     if (*p_sbr != NUL && wp->w_p_wrap && col)
  514.     {
  515.     numberextra = wp->w_p_nu? 8: 0;
  516.     col = (col + numberextra) % Columns;
  517.     if (col == 0 || col + size > (colnr_t)Columns)
  518.     {
  519.         added = STRLEN(p_sbr);
  520.         size += added;
  521.         if (col != 0)
  522.         added = 0;
  523.     }
  524.     }
  525.     if (head != NULL)
  526.     *head = added;
  527.     return size;
  528. }
  529.  
  530. /*
  531.  * Get virtual column number of pos.
  532.  *  start: on the first position of this character (TAB, ctrl)
  533.  * cursor: where the cursor is on this character (first char, except for TAB)
  534.  *    end: on the last position of this character (TAB, ctrl)
  535.  *
  536.  * This is used very often, keep it fast!
  537.  */
  538.     void
  539. getvcol(wp, pos, start, cursor, end)
  540.     WIN        *wp;
  541.     FPOS    *pos;
  542.     colnr_t    *start;
  543.     colnr_t    *cursor;
  544.     colnr_t    *end;
  545. {
  546.     int            col;
  547.     colnr_t        vcol;
  548.     char_u       *ptr;
  549.     int            incr;
  550.     int            head;
  551.     int            ts = wp->w_buffer->b_p_ts;
  552.     int            c;
  553.  
  554.     vcol = 0;
  555.     ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE);
  556.  
  557.     /*
  558.      * This function is used very often, do some speed optimizations.
  559.      * When 'list', 'linebreak' and 'showbreak' are not set use a simple loop.
  560.      */
  561.     if (!wp->w_p_list && !wp->w_p_lbr && *p_sbr == NUL)
  562.     {
  563.     head = 0;
  564.     for (col = pos->col; ; --col, ++ptr)
  565.     {
  566.         c = *ptr;
  567.         /* make sure we don't go past the end of the line */
  568.         if (c == NUL)
  569.         {
  570.         incr = 1;    /* NUL at end of line only takes one column */
  571.         break;
  572.         }
  573.         /* A tab gets expanded, depending on the current column */
  574.         if (c == TAB)
  575.         incr = ts - (vcol % ts);
  576.         else
  577.         incr = CHARSIZE(c);
  578.  
  579.         if (col == 0)    /* character at pos.col */
  580.         break;
  581.  
  582.         vcol += incr;
  583.     }
  584.     }
  585.     else
  586.     {
  587.     for (col = pos->col; ; --col, ++ptr)
  588.     {
  589.         /* A tab gets expanded, depending on the current column */
  590.         head = 0;
  591.         incr = win_lbr_chartabsize(wp, ptr, vcol, &head);
  592.         /* make sure we don't go past the end of the line */
  593.         if (*ptr == NUL)
  594.         {
  595.         incr = 1;    /* NUL at end of line only takes one column */
  596.         break;
  597.         }
  598.  
  599.         if (col == 0)    /* character at pos.col */
  600.         break;
  601.  
  602.         vcol += incr;
  603.     }
  604.     }
  605.     if (start != NULL)
  606.     *start = vcol + head;
  607.     if (end != NULL)
  608.     *end = vcol + incr - 1;
  609.     if (cursor != NULL)
  610.     {
  611.     if (*ptr == TAB && (State & NORMAL) && !wp->w_p_list)
  612.         *cursor = vcol + incr - 1;        /* cursor at end */
  613.     else
  614.         *cursor = vcol + head;        /* cursor at start */
  615.     }
  616. }
  617.  
  618. /*
  619.  * Get the most left and most right virtual column of pos1 and pos2.
  620.  * Used for Visual block mode.
  621.  */
  622.     void
  623. getvcols(pos1, pos2, left, right)
  624.     FPOS    *pos1, *pos2;
  625.     colnr_t *left, *right;
  626. {
  627.     colnr_t    l1, l2, r1, r2;
  628.  
  629.     getvcol(curwin, pos1, &l1, NULL, &r1);
  630.     getvcol(curwin, pos2, &l2, NULL, &r2);
  631.     if (l1 < l2)
  632.     *left = l1;
  633.     else
  634.     *left = l2;
  635.     if (r1 > r2)
  636.     *right = r1;
  637.     else
  638.     *right = r2;
  639. }
  640.  
  641. /*
  642.  * skipwhite: skip over ' ' and '\t'.
  643.  */
  644.     char_u *
  645. skipwhite(p)
  646.     char_u    *p;
  647. {
  648.     while (vim_iswhite(*p)) /* skip to next non-white */
  649.     ++p;
  650.     return p;
  651. }
  652.  
  653. /*
  654.  * skipdigits: skip over digits;
  655.  */
  656.     char_u *
  657. skipdigits(p)
  658.     char_u    *p;
  659. {
  660.     while (isdigit(*p))    /* skip to next non-digit */
  661.     ++p;
  662.     return p;
  663. }
  664.  
  665. /*
  666.  * vim_isdigit: version of isdigit() that can handle characters > 0x100.
  667.  */
  668.     int
  669. vim_isdigit(c)
  670.     int        c;
  671. {
  672.     return (c > 0 && c < 0x100 && isdigit(c));
  673. }
  674.  
  675. /*
  676.  * skiptowhite: skip over text until ' ' or '\t' or NUL.
  677.  */
  678.     char_u *
  679. skiptowhite(p)
  680.     char_u    *p;
  681. {
  682.     while (*p != ' ' && *p != '\t' && *p != NUL)
  683.     ++p;
  684.     return p;
  685. }
  686.  
  687. /*
  688.  * skiptowhite_esc: Like skiptowhite(), but also skip escaped chars
  689.  */
  690.     char_u *
  691. skiptowhite_esc(p)
  692.     char_u    *p;
  693. {
  694.     while (*p != ' ' && *p != '\t' && *p != NUL)
  695.     {
  696.     if ((*p == '\\' || *p == Ctrl('V')) && *(p + 1) != NUL)
  697.         ++p;
  698.     ++p;
  699.     }
  700.     return p;
  701. }
  702.  
  703. /*
  704.  * Getdigits: Get a number from a string and skip over it.
  705.  *
  706.  * Note: the argument is a pointer to a char_u pointer!
  707.  */
  708.  
  709.     long
  710. getdigits(pp)
  711.     char_u **pp;
  712. {
  713.     char_u    *p;
  714.     long    retval;
  715.  
  716.     p = *pp;
  717.     retval = atol((char *)p);
  718.     p = skipdigits(p);        /* skip to next non-digit */
  719.     *pp = p;
  720.     return retval;
  721. }
  722.  
  723. /*
  724.  * Return TRUE if "lbuf" is empty or only contains blanks.
  725.  */
  726.     int
  727. vim_isblankline(lbuf)
  728.     char_u    *lbuf;
  729. {
  730.     char_u    *p;
  731.  
  732.     p = skipwhite(lbuf);
  733.     return (*p == NUL || *p == '\r' || *p == '\n');
  734. }
  735.  
  736. /*
  737.  * Convert a string into a long, taking care of hexadecimal and octal numbers.
  738.  * Returns the number.
  739.  * If "hexp" is not NULL, returns a flag to indicate the type of the number:
  740.  *  0        decimal
  741.  *  '0'        octal
  742.  *  'X'        hex
  743.  *  'x'        hex
  744.  * If "len" is not NULL, the length of the number in characters is returned.
  745.  */
  746.     long
  747. vim_str2nr(start, hexp, len, dooct, dohex)
  748.     char_u    *start;
  749.     int        *hexp;        /* return: type of number 0 = decimal, 'x' or 'X'
  750.                    is hex, '0' = octal */
  751.     int        *len;        /* return: detected length of number */
  752.     int        dooct;        /* recognize octal number */
  753.     int        dohex;        /* recognize hex number */
  754. {
  755.     char_u        *ptr = start;
  756.     int            hex = 0;            /* default is decimal */
  757.     int            negative = FALSE;
  758.     char_u        buf[NUMBUFLEN];
  759.     int            i;
  760.     long        n;
  761.  
  762.     if (ptr[0] == '-')
  763.     {
  764.     negative = TRUE;
  765.     ++ptr;
  766.     }
  767.  
  768.     if (ptr[0] == '0')            /* could be hex or octal */
  769.     {
  770.     hex = ptr[1];
  771.     if (dohex && (hex == 'X' || hex == 'x') && isxdigit(ptr[2]))
  772.         ptr += 2;            /* hexadecimal */
  773.     else
  774.     {
  775.         if (dooct && isdigit(hex))
  776.         hex = '0';        /* octal */
  777.         else
  778.         hex = 0;        /* 0 by itself is decimal */
  779.     }
  780.     }
  781.  
  782.     /*
  783.      * Copy the number into a buffer because some versions of sscanf()
  784.      * cannot handle characters with the upper bit set, making some special
  785.      * characters handled like digits.
  786.      */
  787.     for (i = 0; (hex ? (hex == '0' ? *ptr >= '0' && *ptr <= '7'
  788.              : isxdigit(*ptr)) : isdigit(*ptr)) &&
  789.                                i < NUMBUFLEN - 1; ++i)
  790.     buf[i] = *ptr++;
  791.     buf[i] = NUL;
  792.  
  793.     if (hex == '0')
  794.     sscanf((char *)buf, "%lo", &n);
  795.     else if (hex)
  796.     sscanf((char *)buf, "%lx", &n);        /* "%X" doesn't work! */
  797.     else
  798.     n = atol((char *)buf);
  799.  
  800.     if (hexp != NULL)
  801.     *hexp = hex;
  802.     if (len != NULL)
  803.     *len = ptr - start;
  804.  
  805.     if (negative)
  806.     return -n;
  807.     return n;
  808. }
  809.  
  810. /*
  811.  * Return the value of a single hex character.
  812.  * Only valid when the argument is '0' - '9', 'A' - 'F' or 'a' - 'f'.
  813.  */
  814.     int
  815. hex2nr(c)
  816.     int        c;
  817. {
  818.     if (c >= 'a')
  819.     return c - 'a' + 10;
  820.     if (c >= 'A')
  821.     return c - 'A' + 10;
  822.     return c - '0';
  823. }
  824.